package location

import (
	"strings"

	"device-admin/config/mysql"
	"device-admin/internal/models"

	"192.168.1.75/go-pkg/errorx"
)

type FloorPosition struct{}

// Position 位置点信息，包含楼层名称及品牌logo
type Position struct {
	ID           string  `json:"id" gorm:"column:id"`
	FloorID      string  `json:"floor_id" gorm:"floor_id"`
	PositionName string  `json:"position_name" gorm:"column:position_name"`
	Type         int     `json:"type" gorm:"column:type"`
	PositionX    float64 `json:"position_x" gorm:"column:position_x"`
	PositionY    float64 `json:"position_y" gorm:"column:position_y"`
	Floor        string  `json:"floor,omitempty" gorm:"column:floor"`
	BrandID      string  `json:"brand_id,omitempty" gorm:"column:brand_id"`
	BrandName    string  `json:"brand_name,omitempty" gorm:"column:brand_name"`
	BrandLogo    string  `json:"brand_logo,omitempty" gorm:"column:brand_logo"`
	Keywords     string  `json:"keywords" gorm:"-"`
}

// AddPostion 增加位置点
// floorID 楼层id
func (f FloorPosition) AddPostion(
	floorID string,
	name string,
	positionX float64,
	positionY float64,
	positionType int,
	brandID string,
	keywords string, // 位置关键字，用于搜索
) (string, error) {
	tx := mysql.NewDB().Begin()
	// 判断楼层是否存在
	_, err := Floor{}.FloorExists(floorID)
	if err != nil {
		return "", err
	}
	// 增加位置
	var floorPosition models.FloorPosition
	floorPosition.FloorID = floorID
	floorPosition.Name = name
	floorPosition.PositionX = positionX
	floorPosition.PositionY = positionY
	floorPosition.Type = positionType
	rowsAffected := tx.Model(&models.FloorPosition{}).Create(&floorPosition).RowsAffected
	if rowsAffected <= 0 {
		tx.Rollback()
		return "", errorx.New("新增位置点失败", -1)
	}
	// 增加品牌与位置的关联
	if brandID != "" {
		// 判断是否已存在品牌及楼层的关联
		var floorBrand models.FloorBrand
		rowsAffected = tx.Model(&models.FloorBrand{}).
			Where("brand_id = ?", brandID).
			Where("floor_id = ?", floorID).
			Take(&floorBrand).RowsAffected
		if rowsAffected > 0 {
			rowsAffected := tx.Model(&models.FloorBrand{}).
				Where("id = ?", floorBrand.ID).
				Update("position_id", floorPosition.ID).RowsAffected
			if rowsAffected <= 0 {
				tx.Rollback()
				return "", errorx.New("新增楼层品牌失败", -1)
			}
		} else {
			var floorBrand models.FloorBrand
			floorBrand.FloorID = floorID
			floorBrand.BrandID = brandID
			floorBrand.PositionID = floorPosition.ID
			rowsAffected := tx.Model(&models.FloorBrand{}).Create(&floorBrand).RowsAffected
			if rowsAffected <= 0 {
				tx.Rollback()
				return "", errorx.New("新增楼层品牌失败", -1)
			}
		}
	}
	// 增加位置点关键字
	if keywords != "" {
		keywordArr := strings.Split(keywords, ",")
		records := []models.PositionKeywords{}
		for _, keyword := range keywordArr {
			records = append(records, models.PositionKeywords{
				FloorID:    floorID,
				PositionID: floorPosition.ID,
				Keyword:    keyword,
			})
		}
		tx.Create(&records)
	}
	// 提交事务
	tx.Commit()
	return floorPosition.ID, nil
}

// EditPosistion 编辑位置点
func (FloorPosition) EditPosistion(
	positionID string,
	name string,
	positionX float64,
	positionY float64,
	positionType int,
	brandID string,
	keywords string, // 位置关键字，用于搜索
) (errRet error) {
	tx := mysql.NewDB().Begin()
	// 判断位置点是否存在
	var oldFloorPosition models.FloorPosition
	{
		rowsAffected := tx.Model(&models.FloorPosition{}).Where("id = ?", positionID).Take(&oldFloorPosition).RowsAffected
		if rowsAffected <= 0 {
			tx.Rollback()
			errRet = errorx.New("位置点不存在", -1)
			return
		}
	}

	// 编辑位置点
	{
		var floorPosition models.FloorPosition
		floorPosition.Name = name
		floorPosition.PositionX = positionX
		floorPosition.PositionY = positionY
		floorPosition.Type = positionType
		rowsAffected := tx.Model(&models.FloorPosition{}).Where("id = ?", positionID).Updates(&floorPosition).RowsAffected
		if rowsAffected <= 0 {
			tx.Rollback()
			errRet = errorx.New("位置点更新失败", -1)
			return
		}
	}

	// 更新楼层品牌
	if brandID != "" {
		tx.Model(&models.FloorBrand{}).Where("position_id = ?", positionID).Delete(&models.FloorBrand{})
		var floorBrand models.FloorBrand
		floorBrand.FloorID = oldFloorPosition.FloorID
		floorBrand.BrandID = brandID
		floorBrand.PositionID = positionID
		rowsAffected := tx.Model(&models.FloorBrand{}).Create(&floorBrand).RowsAffected
		if rowsAffected <= 0 {
			tx.Rollback()
			errRet = errorx.New("新增楼层品牌失败", -1)
			return
		}
	}
	// 增加位置点关键字
	if keywords != "" {
		keywordArr := strings.Split(keywords, ",")
		records := []models.PositionKeywords{}
		for _, keyword := range keywordArr {
			records = append(records, models.PositionKeywords{
				FloorID:    oldFloorPosition.FloorID,
				PositionID: positionID,
				Keyword:    keyword,
			})
		}
		tx.Create(&records)
	}
	tx.Commit()
	return nil
}

// DeletePosition 删除位置点
func (FloorPosition) DeletePosition(positionID string) error {
	tx := mysql.NewDB().Begin()
	// 判断位置点是否存在
	var count int64
	tx.Model(&models.FloorPosition{}).
		Where("id = ?", positionID).
		Count(&count)
	if count <= 0 {
		tx.Rollback()
		return errorx.New("位置点不存在", -1)
	}

	// 删除位置点
	rowsAffected := tx.Model(&models.FloorPosition{}).
		Where("id = ?", positionID).
		Delete(&models.FloorPosition{}).RowsAffected
	if rowsAffected <= 0 {
		tx.Rollback()
		return errorx.New("位置点删除失败", -1)
	}

	// 删除对应的楼层商标
	{
		tx.Model(&models.FloorBrand{}).
			Where("position_id = ?", positionID).
			Delete(&models.FloorBrand{})
	}
	tx.Commit()
	return nil
}

// Detail 位置点详情
func (FloorPosition) Detail(positionID string) (Position, error) {
	var position Position
	rowsAffected := mysql.NewDB().Model(&models.FloorPosition{}).
		Select(
			"floor_position.id as id",
			"floor_position.floor_id",
			"floor_position.name as position_name",
			"floor_position.type",
			"floor_position.position_x",
			"floor_position.position_y",
			"floor.Name as floor",
			"brand.name as brand_name",
			"brand.logo as brand_logo",
		).
		Joins("JOIN floor ON floor_position.floor_id = floor.id").
		Joins("LEFT JOIN floor_brand ON floor_brand.position_id = floor_position.id").
		Joins("LEFT JOIN brand ON brand.id = floor_brand.brand_id").
		Where(models.FloorPosition{
			ID: positionID,
		}).Take(&position).RowsAffected
	if rowsAffected <= 0 {
		return Position{}, errorx.New("位置点不存在", -1)
	}
	return position, nil
}

// List 位置列表
// robotID 设备id，locationID 位置id，floorID 楼层id，positionType 位置点类型
func (FloorPosition) List(
	robotID string,
	locationID string,
	floorID string,
	positionType string,
	positionName string,
	brandName string,
	withKeywords bool,
) (positions []Position, err error) {
	db := mysql.NewDB()
	// 查询条件
	tx := db.Model(&models.FloorPosition{}).
		Select(
			"floor_position.id as id",
			"floor_position.floor_id",
			"floor_position.name as position_name",
			"floor_position.type",
			"floor_position.position_x",
			"floor_position.position_y",
			"floor.Name as floor",
			"brand.id as brand_id",
			"brand.name as brand_name",
			"brand.logo as brand_logo",
		).
		Joins("JOIN floor ON floor_position.floor_id = floor.id").
		Joins("LEFT JOIN floor_brand ON floor_brand.position_id = floor_position.id").
		Joins("LEFT JOIN brand ON brand.id = floor_brand.brand_id")
	// 获取设备的位置
	if robotID != "" {
		// 获取设备绑定的楼层信息
		var floorDevice models.FloorDevice
		rowsAffected := db.Model(&models.FloorDevice{}).
			Where("robot_id = ?", robotID).
			Take(&floorDevice).
			RowsAffected
		if rowsAffected <= 0 {
			return nil, errorx.New("设备未绑定楼层信息", -1)
		}

		// 获取楼层的位置
		var floor models.Floor
		rowsAffected = db.Model(&models.Floor{}).
			Where("id = ?", floorDevice.FloorID).
			Take(&floor).RowsAffected
		if rowsAffected <= 0 {
			return nil, errorx.New("楼层不存在", -1)
		}
		locationID = floor.LocationID
		floorID = floor.ID
	}
	// 如果位置不为空，则获取整个商场的信息
	if locationID != "" {
		// 查询商场所有楼层的位置信息
		tx = tx.Where("floor.location_id = ?", locationID)
	} else if floorID != "" {
		// 判断楼层是否存在
		_, err := Floor{}.FloorExists(floorID)
		if err != nil {
			return nil, err
		}
		// 获取指定楼层的位置点
		tx = tx.Where("floor_position.floor_id = ?", floorID)
	}
	// 位置点类型
	positionTypeArr := strings.Split(positionType, ",")
	if len(positionTypeArr) > 0 && positionType != "" {
		tx = tx.Where("floor_position.type IN ?", positionTypeArr)
	}
	// 位置点名称查询
	if positionName != "" {
		tx = tx.Where("floor_position.name LIKE ?", "%"+positionName+"%")
	}
	// 品牌名称
	if brandName != "" {
		tx = tx.Where("brand.name LIKE ?", "%"+brandName+"%")
	}
	// 查询结果
	tx.Find(&positions)
	if !withKeywords {
		return positions, nil
	}
	// 生成位置id的keywordMap
	positionIDs := []string{}
	for _, record := range positions {
		positionIDs = append(positionIDs, record.ID)
	}
	positionKeywords := []models.PositionKeywords{}
	db.Model(&models.PositionKeywords{}).Where("position_id IN ?", positionIDs).Find(&positionKeywords)
	positionKeywordMap := make(map[string][]string)
	for _, record := range positionKeywords {
		positionKeywordMap[record.PositionID] = append(positionKeywordMap[record.PositionID], record.Keyword)
	}
	for index, record := range positions {
		positions[index].Keywords = strings.Join(positionKeywordMap[record.ID], ",")
	}
	// 返回结果
	return positions, nil
}

// 设备详情
type FloorDeviceDetail struct {
	models.FloorDevice
	PositionName string  `json:"position_name" gorm:"column:position_name"`
	PositionX    float64 `json:"position_x" gorm:"column:position_x"`
	PositionY    float64 `json:"position_y" gorm:"column:position_y"`
}

// DeviceList 设备关联列表
func (FloorPosition) DeviceList(floorID string) (floorDeviceDetail []FloorDeviceDetail) {
	mysql.NewDB().Model(&models.FloorDevice{}).
		Select(
			"floor_device.*",
			"floor_position.name as position_name",
			"floor_position.position_x",
			"floor_position.position_y",
		).
		Joins("JOIN floor_position ON floor_device.position_id = floor_position.id").
		Where("floor_device.floor_id = ?", floorID).
		Find(&floorDeviceDetail)
	return floorDeviceDetail
}

// BindDevice 关联设备
func (FloorPosition) BindDevice(robotID string, positionID string) (err error) {
	tx := mysql.NewDB().Begin()
	// 出现错误时回滚
	defer func() {
		if err != nil {
			tx.Rollback()
		}
	}()
	// 设备id不能为空
	if robotID == "" {
		return errorx.New("设备id不能为空", -1)
	}
	// 获取位置点信息
	var floorPosition models.FloorPosition
	{
		rowsAffected := tx.Model(&models.FloorPosition{}).
			Where("id = ?", positionID).
			Take(&floorPosition).RowsAffected
		if rowsAffected <= 0 {
			err = errorx.New("位置点不存在", -1)
			return
		}
	}

	// 删除旧的关联
	{
		tx.Model(&models.FloorDevice{}).
			Where("robot_id = ?", robotID).
			Delete(&models.FloorDevice{})
	}

	// 保存关联
	var floorDevice models.FloorDevice
	floorDevice.FloorID = floorPosition.FloorID
	floorDevice.RobotID = robotID
	floorDevice.PositionID = positionID
	rowsAffected := tx.Model(&models.FloorDevice{}).Create(&floorDevice).RowsAffected
	if rowsAffected <= 0 {
		err = errorx.New("设备关联失败", -1)
		return
	}
	tx.Commit()
	return nil
}

// DeleteBindDevice 删除起始点
func (FloorPosition) DeleteBindDevice(robotID string) error {
	db := mysql.NewDB()
	// 判断设备关联是否存在
	var floorDeviceRecord models.FloorDevice
	rowsAffected := db.Model(&models.FloorDevice{}).Where("robot_id = ?", robotID).Take(&floorDeviceRecord).RowsAffected
	if rowsAffected <= 0 {
		return errorx.New("起始点不存在", -1)
	}
	// 删除旧的关联
	{
		db.Model(&models.FloorDevice{}).
			Where("robot_id = ?", robotID).
			Delete(&models.FloorDevice{})
	}
	// 删除位置点
	{
		db.Model(&models.FloorPosition{}).
			Where("id = ?", floorDeviceRecord.PositionID).
			Delete(&models.FloorPosition{})
	}
	return nil
}

// RemoveDevice 取消设备关联
func (FloorPosition) RemoveDevice(robotID string) error {
	db := mysql.NewDB()
	rowsAffected := db.Model(&models.FloorDevice{}).Where(models.FloorDevice{
		RobotID: robotID,
	}).Delete(&models.FloorDevice{}).RowsAffected
	if rowsAffected <= 0 {
		return errorx.New("删除旧的设备关联失败")
	}
	return nil
}

// SetDevicePosition 设置设备位置
func (FloorPosition) SetDevicePosition(robotID string, positionID string) error {
	db := mysql.NewDB()

	var device models.Device
	db.Model(&models.Device{}).Where("robot_id = ?", robotID).Take(&device)

	var floor models.Floor
	db.Model(&models.FloorPosition{}).
		Joins("JOIN floor ON floor.id = floor_position.floor_id").
		Select("floor.location_id").
		Take(&floor)

	if device.LocationID != floor.LocationID {
		return errorx.New("位置与分配的商场不符", -1)
	}

	rowsAffected := db.Model(&models.FloorDevice{}).
		Where(models.FloorDevice{
			RobotID: robotID,
		}).
		Update("position_id", positionID).RowsAffected
	if rowsAffected <= 0 {
		return errorx.New("设置设备位置失败")
	}
	return nil
}
